using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

using Impact.Controllers;
using Impact.Infrastructure;
using Impact.Infrastructure.Logging;
using Impact.Model.Objects;
using Impact.Model.Service;
using Impact.Web.Admin.Controls;
using Impact.Web.Foundation.Controls;

using StructureMap;

namespace Impact.Web.Admin
{
    public partial class UploadRetailers : Impact.Web.Foundation.PageBase
    {

        /// <summary>
        /// loading event handler
        /// </summary>
        protected override void OnLoad( EventArgs e )
        {
            base.OnLoad( e );

            if ( IsPostBack && !string.IsNullOrEmpty( this.Contents ) )
            {
                LoadColumnControls();
            }

        }

        /// <summary>
        /// event handler executed when the user clicks "Upload"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void UploadCommand( object sender, CommandEventArgs e )
        {
            using ( StreamReader reader = new StreamReader( uploadFile.PostedFile.InputStream ) )
            {
                this.Contents = reader.ReadToEnd();
                reader.Close();
            }

            LoadColumnControls();

            filePanel.Visible = false;
            columnPanel.Visible = true;
        }

        private void LoadColumnControls()
        {
            fieldSelectPanel.Controls.Clear();

            string firstLine = this.Contents.Substring( 0, this.Contents.IndexOf( '\n' ) );
            string[] columns = ParseFields( firstLine );

            if ( columns.Length > 0 )
            {
                columnNumberLabel.Text = columns.Length.ToString();
                for ( int i = 0; i < columns.Length; i++ )
                {
                    HtmlTableRow row = new HtmlTableRow();
                    HtmlTableCell cell = new HtmlTableCell();
                    cell.InnerText = string.Format( "Column #{0}", i + 1 );
                    row.Controls.Add( cell );

                    cell = new HtmlTableCell();
                    RetailerFieldDropDown field = (RetailerFieldDropDown)this.LoadControl( "~/Admin/Controls/RetailerFieldDropDown.ascx" );
                    field.ID = string.Format( "column{0}", i );
                    field.Index = i;
                    cell.Controls.Add( field );
                    row.Controls.Add( cell );

                    fieldSelectPanel.Controls.Add( row );
                }
            }
            else
            {
                columnNumberLabel.Visible = false;
                message.Text = "Could not parse file.";
                message.ForeColor = System.Drawing.Color.Red;
            }

        }

        private string[] ParseFields( string input )
        {
            string pattern = "((\"(?<field>[^\"]*)\")|(?<field>[^,]*)),?";

            List<string> fields = new List<string>();
            Match match = Regex.Match( input, pattern );

            while ( match != null && match.Success )
            {
                Group field = match.Groups["field"];
                if ( field != null )
                    fields.Add( field.Value );

                match = match.NextMatch();
            }
            return fields.ToArray();
        }

        /// <summary>
        /// event handler executed when the user clicks Save
        /// </summary>
        protected void SaveCommand( object sender, CommandEventArgs e )
        {
            IProfileService service = ObjectFactory.GetInstance<IProfileService>();

            List<Control> fieldControls = Utility.FindControlsRecursive( fieldSelectPanel.Controls, typeof( RetailerFieldDropDown ) );
            Dictionary<string, int> fields = new Dictionary<string, int>();
            foreach ( RetailerFieldDropDown control in fieldControls )
            {
                if ( !control.SelectedValue.Equals( "Ignore" ) )
                {
                    if ( !fields.ContainsKey( control.SelectedValue ) )
                        fields.Add( control.SelectedValue, control.Index );
                    else
                    {
                        message.Text = "A field may only be mapped to ONE column";
                        message.ForeColor = System.Drawing.Color.Red;
                        return;
                    }
                }
            }

            if ( !fields.ContainsKey( Profile.Columns.CompanyName ) )
            {
                message.Text = "One field MUST be mapped to Company Name";
                message.ForeColor = System.Drawing.Color.Red;
                return;
            }

            int succeeded = 0;
            int lineNumber = 0;
            Dictionary<int, Exception> failures = new Dictionary<int, Exception>();

            string[] lines = this.Contents.Split( new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries );

            foreach ( string line in lines )
            {
                string[] parts = null;
                try
                {
                    lineNumber++;
                    if ( !string.IsNullOrEmpty( line ) )
                    {
                        parts = ParseFields( line );

                        string companyName = parts[fields[Profile.Columns.CompanyName]];
                        if ( !string.IsNullOrEmpty( companyName ) )
                        {
                            // try to load existing profile
                            Profile profile = service.FetchProfile( Profile.Columns.CompanyName, companyName );
                            // else create a new one
                            if ( profile == null ) profile = new Profile();

                            profile.CompanyName = companyName;

                            if ( fields.ContainsKey( Profile.Columns.Address ) )
                            {
                                profile.Address = parts[fields[Profile.Columns.Address]];
                            }
                            if ( fields.ContainsKey( Profile.Columns.City ) )
                            {
                                profile.City = parts[fields[Profile.Columns.City]];
                            }
                            if ( fields.ContainsKey( Profile.Columns.State ) )
                            {
                                string state = parts[fields[Profile.Columns.State]];
                                if ( state.Length == 2 )
                                    profile.State = state;
                            }

                            bool zipChanged = false;
                            if ( fields.ContainsKey( Profile.Columns.Zipcode ) )
                            {
                                string zip = parts[fields[Profile.Columns.Zipcode]];
                                if ( profile.Zipcode != zip )
                                {
                                    profile.Zipcode = zip;
                                    zipChanged = true;
                                }
                            }

                            if ( profile.IsNew )
                            {
                                profile.Enabled = true;
                            }
                            service.Save( profile, UserId );

                            if ( profile.IsNew || zipChanged || profile.Latitude == 0.0F || profile.Longitude == 0.0F )
                            {
                                BackgroundWorker worker = new BackgroundWorker();
                                worker.DoWork += new DoWorkEventHandler( LookupZipcode );
                                worker.RunWorkerAsync( profile.ProfileID );
                            }

                            succeeded++;

                        }
                    }
                }
                catch ( Exception ex )
                {
                    failures.Add( lineNumber, ex );
                }
            }

            StringBuilder failureText = new StringBuilder();
            foreach ( int key in failures.Keys )
            {
                failureText.AppendFormat( "Line {0}: {1}<br/>", key, failures[key].Message );
            }

            if ( failures.Count > 0 )
            {
                message.Text = string.Format( "Imported: {0}<br/>Failed: {1}<br/>Reasons:<br/>{2}",
                    succeeded, failures.Count, failureText.ToString() );
                message.ForeColor = System.Drawing.Color.Red;
            }
            else
            {
                message.Text = string.Format( "Imported: {0}",
                    succeeded, failures.Count, failureText.ToString() );
                message.ForeColor = System.Drawing.Color.Green;
            }

            columnPanel.Visible = false;
            filePanel.Visible = false;
            closePanel.Visible = true;

            Logger.Write( message.Text, System.Diagnostics.TraceEventType.Information );

        }

        void LookupZipcode( object sender, DoWorkEventArgs e )
        {
            IProfileService service = ObjectFactory.GetInstance<IProfileService>();
            int id = (int)e.Argument;
            Profile profile = service.FetchProfile( id );
            if ( profile != null )
            {
                float lat, lng;
                string loc;
                if ( LocationController.GetLocationFromZip( profile.Zipcode, out lat, out lng, out loc ) )
                {
                    profile.Latitude = lat;
                    profile.Longitude = lng;
                    service.Save( profile, UserId );
                }
                else
                {
                    // log and swallow, cuz we're on an independent thread
                    Logger.Write( string.Format( "Profile ID: {0} - Invalid Zipcode: {1}", profile.ProfileID, profile.Zipcode ),
                        System.Diagnostics.TraceEventType.Error );
                }
            }

        }

        string Contents
        {
            get { return Session["fileContents"] as string; }
            set { Session["fileContents"] = value; }
        }

    }
}
